{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Configuring backend"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Configuration Files\n",
    "\n",
    "Like the Jupyter Notebook server, JupyterHub, and other Jupyter interactive\n",
    "computing tools, `jupyter-lsp` can be configured via [Python or JSON\n",
    "files][notebook-config] in _well-known locations_. You can find out where to put\n",
    "them on your system with:\n",
    "\n",
    "[notebook-config]: https://jupyter-notebook.readthedocs.io/en/stable/config.html\n",
    "\n",
    "```bash\n",
    "jupyter --paths\n",
    "```\n",
    "\n",
    "They will be merged from bottom to top, and the directory where you launch your\n",
    "`notebook` or `lab` server wins, making it easy to check in to version control."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Configuration Options"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### language_servers\n",
    "\n",
    "`jupyter-lsp` does not come with any Language Servers! However, we will try to\n",
    "use [known language servers](./Language%20Servers.html) if they _are_ installed\n",
    "and we know about them. You can disable auto-detection behavior by configuring\n",
    "[autodetect](#autodetect).\n",
    "\n",
    "If you did not find an implementation for the language server you need on the\n",
    "list of [known language servers](./Language%20Servers.html), continue reading!\n",
    "\n",
    "> Please consider [contributing your language server spec](./Contributing.html)\n",
    "> to `jupyter-lsp`!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The absolute minimum language server spec requires:\n",
    "\n",
    "- `argv`, a list of _shell tokens_ to launch the server in `stdio` mode (as\n",
    "  opposed to `tcp`),\n",
    "  - _shell tokens_ are arrays of strings representing command line commands with\n",
    "    arguments, for example `ls -l` is represented as `[\"ls\", \"-l\"]` while\n",
    "    `mkdir \"new directory\"` should be split into `[\"mkdir\", \"new directory\"]`;\n",
    "    If you have Python installed, you can use `shlex.split(\"your command\")` to\n",
    "    get such an array.\n",
    "- the `languages` which the language server will respond to, and\n",
    "- the schema `version` of the spec (currently `2`)\n",
    "- `mime_types` by which the notebooks and files will be matched to the language\n",
    "  server:\n",
    "  - for notebooks the MIME type is derived from `language_info`/`mimetype`\n",
    "    element of [kernel_info][] response (with fallback on to cell metadata if\n",
    "    missing from kernel response)\n",
    "  - for files the implementation is frontend-specific; in JupyterLab the MIME\n",
    "    type is obtained from: a) the code editor MIME type registry, which is by\n",
    "    default using the CodeMirror mode as for JupyterLab 3.x, or if no specific\n",
    "    MIME type is found there, then b) from the `DocumentRegistry` file type by\n",
    "    matching the `contentsModel` against the registered file types using\n",
    "    `getFileTypeForModel()` method (if multiple MIME types are present, the\n",
    "    first one will be used).\n",
    "- `requires_documents_on_disk` should be `false` for all new specifications, as\n",
    "  any code paths requiring documents on disks should be fixed in the LSP servers\n",
    "  rather than masked by using the `.virtual_documents` workaround.\n",
    "\n",
    "```python\n",
    "# ./jupyter_server_config.json                   ---------- unique! -----------\n",
    "#                                               |                              |\n",
    "# or e.g.                                       V                              V\n",
    "# $PREFIX/etc/jupyter/jupyter_server_config.d/a-language-server-implementation.json\n",
    "{\n",
    "  \"LanguageServerManager\": {\n",
    "    \"language_servers\": {\n",
    "      \"a-language-server-implementation\": {\n",
    "        \"version\": 2,\n",
    "        \"argv\": [\"/absolute/path/to/a-language-server\", \"--stdio\"],\n",
    "        \"languages\": [\"a-language\"],\n",
    "        \"mime_types\": [\"text/language\", \"text/x-language\"],\n",
    "        \"display_name\": \"My LSP server\"\n",
    "      }\n",
    "    }\n",
    "  }\n",
    "}\n",
    "```\n",
    "\n",
    "[kernel_info]:\n",
    "  https://jupyter-client.readthedocs.io/en/stable/messaging.html#kernel-info"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The documentation of `display_name` along many other properties is available in\n",
    "the [schema][]. Please note that some of the properties defined in the schema\n",
    "are intended for future use: we would like to use them to enrich the user\n",
    "experience but we prioritized other features for now. We welcome any help in\n",
    "creating the user interface making use of these properties.\n",
    "\n",
    "[schema]:\n",
    "  https://github.com/jupyter-lsp/jupyterlab-lsp/blob/master/python_packages/jupyter_lsp/jupyter_lsp/schema/schema.json\n",
    "\n",
    "More complex configurations that can't be hard-coded may benefit from the python\n",
    "approach:\n",
    "\n",
    "```python\n",
    "# jupyter_server_config.py\n",
    "import shutil\n",
    "\n",
    "# c is a magic, lazy variable\n",
    "c.LanguageServerManager.language_servers = {\n",
    "    \"a-language-server-implementation\": {\n",
    "        # if installed as a binary\n",
    "        \"argv\": [shutil.which(\"a-language-server\")],\n",
    "        \"languages\": [\"a-language\"],\n",
    "        \"version\": 2,\n",
    "        \"mime_types\": [\"text/a-language\"],\n",
    "        \"display_name\": \"A language server\"\n",
    "    },\n",
    "    \"another-language-implementation\": {\n",
    "        # if run like a script\n",
    "        \"argv\": [shutil.which(\"another-language-interpreter\"), \"another-language-server\"],\n",
    "        \"languages\": [\"another-language\"],\n",
    "        \"version\": 2,\n",
    "        \"mime_types\": [\"text/another-language\"],\n",
    "        \"display_name\": \"Another language server\"\n",
    "    }\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### nodejs\n",
    "\n",
    "> default: `None`\n",
    "\n",
    "An absolute path to your `nodejs` executable. If `None`, `nodejs` will be\n",
    "detected in a number of well-known places."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### autodetect\n",
    "\n",
    "> default: `True`\n",
    "\n",
    "If `True`, `jupyter-lsp` will look for all\n",
    "[known language servers](./Language%20Servers.html). User-configured\n",
    "`language_servers` of the same implementation will be preferred over\n",
    "`autodetect`ed ones."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### node_roots\n",
    "\n",
    "> default: `[]`\n",
    "\n",
    "Absolute paths to search for directories named `node_modules`, such as\n",
    "`nodejs`-backed language servers. The order is, roughly:\n",
    "\n",
    "- the folder where `notebook` or `lab` was launched\n",
    "- the JupyterLab `staging` folder\n",
    "- wherever `conda` puts global node modules\n",
    "- wherever some other conventions put it"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### extra_node_roots\n",
    "\n",
    "> default: `[]`\n",
    "\n",
    "Additional places `jupyter-lsp` will look for `node_modules`. These will be\n",
    "checked _before_ `node_roots`, and should not contain the trailing\n",
    "`node_modules`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### virtual_documents_dir\n",
    "\n",
    "> default: os.getenv(\"JP_LSP_VIRTUAL_DIR\", \".virtual_documents\")\n",
    "\n",
    "Path (relative to the content manager root directory) where a transient copy of\n",
    "the virtual documents should be written allowing LSP servers to access the file\n",
    "using operating system's file system APIs if they need so (which is\n",
    "discouraged).\n",
    "\n",
    "Its default value can be set with `JP_LSP_VIRTUAL_DIR` environment variable. The\n",
    "fallback value is `.virtual_documents`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Python `entry_points`\n",
    "\n",
    "`pip`-installable packages in the same environment as the Jupyter server can be\n",
    "automatically detected as providing [language_servers](#language_servers). These\n",
    "are a little more involved, but also more powerful: see more in\n",
    "[Contributing](Contributing.html#Specs). Servers configured this way are loaded\n",
    "_before_ those defined in [configuration files](#Configuration-Files), so that a\n",
    "user can fine-tune their available servers."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Making Custom Servers Work With Notebooks\n",
    "\n",
    "To enable integration of language servers with Jupyter notebooks this extensions\n",
    "assumes that the `language_info` section of [`kernel_info_reply`][kernel_info]\n",
    "is complete and properly returned by the Kernel. In particular the following\n",
    "elements are required:\n",
    "\n",
    "- **File extension**: many language servers only handle files with specific file\n",
    "  extensions and refuse to operate if not provided with such; the file extension\n",
    "  of a native script for a given language (this is other than `.ipynb`), derived\n",
    "  from `file_extension` field of `language_info`, will be added to the name of\n",
    "  the notebook when communicating with the language server to satisfy the file\n",
    "  extension check.\n",
    "- **MIME type**: matching of notebooks to servers is based on the MIME types\n",
    "  declared in server specification files and `mimetype` field of\n",
    "  `language_info`. If kernel fails to provide any MIME type, connecting the\n",
    "  language server will not be possible; if multiple MIME types are in use, any\n",
    "  would work well for this extension as long as you also include it in the\n",
    "  `mime_types` list of language server specification."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Example: Scala Language Server (metals) integration\n",
    "\n",
    "**Step 1:** Get a Scala-based kernel installed.\n",
    "\n",
    "2 possible options: Almond kernel or the Spark magic kernel.\n",
    "\n",
    "Almond kernel install:\n",
    "\n",
    "```bash\n",
    "$ curl -Lo coursier https://git.io/coursier-cli\n",
    "$ chmod +x coursier\n",
    "$ ./coursier launch --fork almond -- --install\n",
    "$ rm -f coursier\n",
    "```\n",
    "\n",
    "Spark Magic kernel:\n",
    "\n",
    "```bash\n",
    "pip install sparkmagic\n",
    "```\n",
    "\n",
    "Now, install the spark kernel:\n",
    "\n",
    "```bash\n",
    "jupyter-kernelspec install sparkmagic/kernels/sparkkernel\n",
    "```\n",
    "\n",
    "**Step 2:** Install metals server in the working directory:\n",
    "\n",
    "Metals has a coursier based installation.\n",
    "\n",
    "```bash\n",
    "curl -Lo coursier https://git.io/coursier-cli && chmod +x coursier\n",
    "./coursier bootstrap org.scalameta:metals_2.12:0.7.0 --force-fetch -o metals -f\n",
    "```\n",
    "\n",
    "(Might need to use the `--force-fetch` flag if you are getting dependency\n",
    "issues.)\n",
    "\n",
    "**Step 3:** Configure the metals server in jupyterlab-lsp. Enter the following\n",
    "in the `jupyter/jupyter_server_config.d/metals-ls.json` (in one of the jupyter\n",
    "configuration directories):\n",
    "\n",
    "```python\n",
    "{\n",
    "  \"LanguageServerManager\": {\n",
    "    \"language_servers\": {\n",
    "      \"metals\": {\n",
    "        \"version\": 2,\n",
    "        \"argv\": [\"<$path_to_metals_server(eg:/Users/skakker/projects/jupyterlab-lsp/metals)>\"],\n",
    "        \"languages\": [\"scala\"],\n",
    "        \"mime_types\": [\"text/x-scala\"]\n",
    "      }\n",
    "    }\n",
    "  }\n",
    "}\n",
    "```\n",
    "\n",
    "You are good to go now! Just start `jupyter lab` and create a notebook with\n",
    "either the Spark or the Scala kernel and the metals server should automatically\n",
    "initialise (the status indicator should show \"Fully initialized\")."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.6"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
